<template>
  <div ref="editorContainer" class="editor"></div>
</template>
<script>
const beautify = require("js-beautify");
import { setLocaleData } from "monaco-editor-nls";
import zh_CN from "monaco-editor-nls/locale/zh-hans";
setLocaleData(zh_CN);
// const monaco = require("monaco-editor");
// 右键菜单
import * as actions from "monaco-editor/esm/vs/platform/actions/common/actions";

// const monaco = require("monaco-editor/esm/vs/editor/editor.api");
// 引入language时需要这种 使用下面的方式引入  不能require
import * as monaco from "monaco-editor/esm/vs/editor/editor.api.js";

// 1. 引入monaco-editor中的python文件
import { language as htmlLanguage } from "monaco-editor/esm/vs/basic-languages/html/html.js";
import { language as cssLanguage } from "monaco-editor/esm/vs/basic-languages/css/css.js";
import { language as javascriptLanguage } from "monaco-editor/esm/vs/basic-languages/javascript/javascript.js";
import { language as myJavascriptLanguage } from "./javascript";

// import "monaco-editor/esm/vs/basic-languages/html/html.js";
// import "monaco-editor/esm/vs/basic-languages/css/css.js";
// import "monaco-editor/esm/vs/basic-languages/javascript/javascript.js";

const beautify_js = beautify.js;
const beautify_css = beautify.css;
const beautify_html = beautify.html;

// 格式化配置选项
const beautify_js_options = {
  indent_size: "4",
  indent_char: " ",
  max_preserve_newlines: "5",
  preserve_newlines: true,
  keep_array_indentation: true,
  keep_function_indentation: true,
  break_chained_methods: false,
  indent_scripts: "normal",
  brace_style: "none",
  space_before_conditional: true,
  unescape_strings: false,
  jslint_happy: false,
  end_with_newline: true,
  wrap_line_length: "0",
  indent_inner_html: false,
  comma_first: false,
  e4x: false,
  indent_empty_lines: false,
};
export default {
  name: "MonacoEditor",
  data() {
    return {
      code: [
        "<style>",
        "    body {",
        "        color: red;",
        "        align-self: start;",
        "        background: #000;",
        "    }",
        "</style>",
        "",
        "",
        "<div class='box'>",
        "    as   d2f",
        "    sadf w   ef wd qwef",
        "    fsd f",
        "</div>",
        "<script>",
        "    let a = []",
        "          a.forEach((item) => {",
        "             console.log(item)",
        "    })",
        "<\/script>",
      ].join("\n"),
      editor: null,
    };
  },
  mounted() {
    this.init();
  },
  methods: {
    init() {
      // 初始化编辑器
      this.editor = monaco.editor.create(this.$refs.editorContainer, {
        value: this.code,
        language: "html",
        tabSize: 2,
        theme: "vs-dark",
        autoOpenPopover: true, // 是否自动打开工具提示菜单，这里是true
        automaticLayout: true,
        // formatOnPaste: true, // 粘贴格式化
        // formatOnType: true,
        suggestOnTriggerCharacters: true,
        wordWrap: true, // 是否开启自动换行功能，这里是true
        dragAndDrop: true, // 是否开启拖拽功能，这里是true
        maxLines: Infinity, // 最大行数，这里是Infinity（无限）
        minimap: { enabled: false }, // 隐藏代码缩图
        // contextmenu: false, // 是否显示右键菜单
      });
      console.log(this.editor);

      // 监听内容变化
      this.editor.onDidChangeModelContent((e) => {
        this.code = this.getValue();
        console.log("监听内容变化", this.code);
      });
      // 监听失去焦点事件
      this.editor.onDidBlurEditorText((e) => {
        console.log(e);
      });

      this.removeActions();
      this.addCustomContextMenu();
      this.registerCompletion();
    },
    registerCompletion() {
      let monaco2 = this.editor;

      // javascript
      monaco.languages.registerCompletionItemProvider("html", {
        provideCompletionItems: (model, position, context, token) => {
          console.log(
            "model, position,context, token: ",
            model,
            position,
            context,
            token
          );
          var textUntilPosition = model.getValueInRange({
            startLineNumber: position.lineNumber,
            startColumn: 1,
            endLineNumber: position.lineNumber,
            endColumn: position.column,
          });
          // let currentrow = this.getCurrentRow(textUntilPosition);
          let currentrow = textUntilPosition;
          // 去除多余空格
          const trimmedString = this.code.replace(/[\r\n]/g, "");
          console.log("trimmedString ", trimmedString);

          // 根据标签截断字符串
          const parts = trimmedString.split(/(<\/?style>|<\/?script>)/);
          console.log("parts ", parts);
          let tag = "html";
          for (let i = 0; i < parts.length; i++) {
            const part = parts[i].trim();

            if (part.includes(currentrow)) {
              console.log(parts[i - 1]);
              if (parts[i - 1].indexOf("<style") > -1) {
                tag = "style";
                console.log(`该行数据：'${currentrow}' 在 <style> 标签中`);
              } else if (parts[i - 1].indexOf("<script") > -1) {
                tag = "script";
                console.log(`该行数据：'${currentrow}' 在 <script> 标签中`);
              } else {
                console.log(`该行数据：'${currentrow}' 在 <html> 标签中`);
              }
            }
          }

          let suggestions = [];
          if (tag == "script") {
            suggestions = this.registerJavascript();
          } else if (tag == "style") {
            suggestions = this.registerCss();
          } else if (tag == "html") {
            suggestions = this.registerHtml();
          }
          console.log("suggestions: ", suggestions);

          return {
            // 最后要返回一个数组
            suggestions: suggestions,
          };
        },
      });
    },

    getCurrentRow(textUntilPosition) {
      //过滤特殊字符
      let _textUntilPosition = textUntilPosition
        .replace(/[\*\[\]@\$\(\)]/g, "")
        .replace(/(\s+|\.)/g, " ");
      //切割成数组
      let arr = _textUntilPosition.split(" ");
      //取当前输入值
      let activeStr = arr[arr.length - 1];
      //获得输入值的长度
      let len = activeStr.length;

      //获得编辑区域内已经存在的内容
      let rexp = new RegExp("([^\\w]|^)" + activeStr + "\\w*", "gim");
      let match = that.value.match(rexp);
      let _hints = !match
        ? []
        : match.map((ele) => {
            let rexp = new RegExp(activeStr, "gim");
            let search = ele.search(rexp);
            return ele.substr(search);
          });

      //查找匹配当前输入值的元素
      let hints = Array.from(new Set([...that.hints, ..._hints]))
        .sort()
        .filter((ele) => {
          let rexp = new RegExp(ele.substr(0, len), "gim");
          return (match && match.length === 1 && ele === activeStr) ||
            ele.length === 1
            ? false
            : activeStr.match(rexp);
        });
      //添加内容提示
      let res = hints.map((ele) => {
        return {
          label: ele,
          kind:
            that.hints.indexOf(ele) > -1
              ? monaco.languages.CompletionItemKind.Keyword
              : monaco.languages.CompletionItemKind.Text,
          documentation: ele,
          insertText: ele,
        };
      });
      return res;
    },
    /**
     * 注册html语法
     */
    registerHtml() {},
    /**
     * 注册css语法
     */
    registerCss() {
      // 注册CSS语言
      monaco.languages.register({ id: "css" });
      // 注册CSS语言配置
      monaco.languages.setMonarchTokensProvider("css", cssLanguage);
      return;
      monaco.languages.registerCompletionItemProvider("html", {
        provideCompletionItems: function () {
          let suggestions = [];
          // 这个keywords就是python.js文件中有的
          cssLanguage.keywords.forEach((item) => {
            suggestions.push({
              label: item,
              kind: monaco.languages.CompletionItemKind.Keyword,
              insertText: item,
            });
          });

          return {
            // 最后要返回一个数组
            suggestions: suggestions,
          };
        },
      });
    },
    /**
     *注册js语法
     */
    registerJavascript() {
      let arr = [];
      // 这个keywords就是python.js文件中有的
      for (let index = 0; index < javascriptLanguage.keywords.length; index++) {
        const item = javascriptLanguage.keywords[index];
        arr.push({
          label: item,
          kind: monaco.languages.CompletionItemKind.Keyword,
          insertText: item,
        });
      }

      for (const [langKey, langValue] of Object.entries(myJavascriptLanguage)) {
        arr.push({
          label: langValue.prefix,
          kind: monaco.languages.CompletionItemKind.Keyword,
          insertText: langValue.body.join("\n"),
        });
      }
      return arr;
    },
    // 移除所有右键菜单
    removeActions() {
      let menus = actions.MenuRegistry._menuItems;
      console.log("menus: ", menus);
      // 注意 entry[0].id
      let contextMenuEntry = [...menus].find((entry) => {
        console.log("entry: ", entry);

        return entry[0]._debugName == "EditorContext";
      });
      console.log("contextMenuEntry: ", contextMenuEntry);

      let contextMenuLinks = contextMenuEntry[1];

      // 感谢群里同学的贡献
      let removableIds = [
        "editor.action.clipboardCutAction",
        "editor.action.clipboardCopyAction",
        "editor.action.clipboardPasteAction",
        "editor.action.refactor",
        "editor.action.sourceAction",
        "editor.action.revealDefinition",
        "editor.action.revealDeclaration",
        "editor.action.goToTypeDefinition",
        "editor.action.goToImplementation",
        "editor.action.goToReferences",
        "editor.action.formatDocument",
        "editor.action.formatSelection",
        "editor.action.changeAll",
        "editor.action.rename",
        "editor.action.quickOutline",
        "editor.action.quickCommand",
        "Peek",
      ];

      let removeById = (list, ids) => {
        let node = list._first;
        do {
          // 注意 node.element?.command?.id || node.element?.title 的用法
          let shouldRemove = ids.includes(
            node.element?.command?.id || node.element?.title
          );
          if (shouldRemove) {
            list._remove(node);
          }
        } while ((node = node.next));
      };

      removeById(contextMenuLinks, removableIds);
    },
    // 获取编辑框内容
    getValue() {
      const content = this.editor.getValue();
      console.log("获取编辑器内容:", content);
      return content;
    },
    // 设置编辑框内容
    handleSetContent(val) {
      this.editor.setValue(val);
    },
    // 自动格式化代码
    format() {
      this.editor.trigger("anything", "editor.action.formatDocument"); // 或者
      // this.editor.getAction(['editor.action.formatDocument']).run()
    },
    changeEditor() {
      if (this.editor === null) {
        this.init();
      }
      const oldModel = this.editor.getModel();
      const newModel = monaco.editor.createModel(this.code, "javascript");
      if (oldModel) {
        oldModel.dispose();
      }
      this.editor.setModel(newModel);
    },
    addCustomContextMenu() {
      let editor = this.editor;
      editor.addAction({
        id: "my-format-document",
        label: "格式化文档",
        contextMenuGroupId: "navigation",
        keybindings: [monaco.KeyMod.CtrlCmd | monaco.KeyCode.KEY_F],
        run: () => {
          editor.getAction("editor.action.formatDocument").run();
          const parser = new DOMParser();
          const doc = parser.parseFromString(this.code, "text/html");
          // 获取所有的<script>标签
          const scriptTags = doc.querySelectorAll("script");
          // 遍历每个<script>标签，并格式化其中的 JavaScript 代码块
          scriptTags.forEach((scriptTag) => {
            const scriptContent = scriptTag.textContent;
            const formattedScriptContent = beautify_js(
              scriptContent,
              beautify_js_options
            );
            console.log("formattedScriptContent: ", formattedScriptContent);

            // 将格式化后的内容更新到<script>标签中
            scriptTag.textContent = formattedScriptContent;
          });

          // 获取格式化后的富文本内容
          // const formattedRichTextContent = doc.documentElement.outerHTML;
          const formattedRichTextContent = doc.body.innerHTML;
          this.code = formattedRichTextContent;
          this.handleSetContent(this.code);
          console.log("formattedRichTextContent: ", formattedRichTextContent);
        },
      });

      editor.addAction({
        id: "my-format-selection",
        label: "格式化选中内容",
        contextMenuGroupId: "navigation",
        keybindings: [monaco.KeyMod.CtrlCmd | monaco.KeyCode.KEY_S],
        run: () => {
          editor.getAction("editor.action.formatSelection").run();
        },
      });
      editor.addAction({
        id: "my-custom-action",
        label: "My Custom Action",
        contextMenuGroupId: "navigation",
        run: function (ed) {
          console.log("My custom action was clicked!");
        },
      });

      // this.editor.onContextMenu((e) => {
      //   console.log("e: ", e);
      //   e.event.preventDefault();
      //   editor.focus();

      //   const actions = ["format-document", "format-selection"].map((id) => ({
      //     id,
      //     label: editor.getAction(id).label,
      //   }));

      //   editor.action.showContextMenu(actions);
      // });
    },
  },
};
</script>

<style scoped>
.editor {
  width: 100%;
  min-height: 400px;
}
</style>
